﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using System.IO;
using System.Globalization;

struct Stop
{
    public string ID;
    public string Name;
    public double Latitude;
    public double Longitude;

    public Stop(string gtfsLine)
    {
        // Split up the comma-separated values from the GTFS data line
        string[] gtfsElements = gtfsLine.Split(',');

        // Set the internal properties
        this.ID = gtfsElements[0];
        this.Name = gtfsElements[2];
        this.Latitude = Convert.ToDouble(gtfsElements[4]);
        this.Longitude = Convert.ToDouble(gtfsElements[5]);
    }

    public override string ToString()
    {
        return Name;
    }
}

struct StopTime
{
    public string TripID;
    public DateTime DepartureTime;
    public string StopID;
    public int StopSequence;

    public StopTime(string gtfsLine)
    {
        // Split up the comma-separated values from the GTFS data line
        string[] gtfsElements = gtfsLine.Split(',');

        // Set the internal fields
        this.TripID = gtfsElements[0];
        this.StopSequence = Convert.ToInt32(gtfsElements[4]);

        this.DepartureTime = DateTime.Today;
        string[] hms = gtfsElements[2].Split(':');

        this.DepartureTime = this.DepartureTime.AddHours(Convert.ToInt32(hms[0]));
        this.DepartureTime = this.DepartureTime.AddMinutes(Convert.ToInt32(hms[1]));
        this.DepartureTime = this.DepartureTime.AddSeconds(Convert.ToInt32(hms[2]));

        while (DateTime.Now.CompareTo(this.DepartureTime) > 0)
        {
            this.DepartureTime = this.DepartureTime.AddDays(1);
        }

        this.StopID = gtfsElements[3];
    }
}

struct Trip
{
    public string ID;
    public string Headsign;
    public int Direction;
    public string RouteID;
    public string ServiceID;

    public Trip(string gtfsLine)
    {
        // Split up the comma-separated values from the GTFS data line
        string[] gtfsElements = gtfsLine.Split(',');

        // Set the ID, Number and Name properties
        this.ID = gtfsElements[2];
        this.Headsign = gtfsElements[3];
        this.Direction = Convert.ToInt32(gtfsElements[4]);
        this.RouteID = gtfsElements[0];
        this.ServiceID = gtfsElements[1];
    }
}

struct CalendarDate
{
    public string ServiceID;
    public DateTime ServiceDay;

    public CalendarDate(string gtfsLine)
    {
        // Split up the comma-separated values from the GTFS data line
        string[] gtfsElements = gtfsLine.Split(',');

        // Set the ID and Date properties
        this.ServiceID = gtfsElements[0];
        this.ServiceDay = DateTime.ParseExact(gtfsElements[1], "yyyyMMdd", CultureInfo.InvariantCulture);
    }
}

struct Route
{
    public string ID;
    public int Number;
    public string Name;
    public Color Color;
    public Color TextColor;

    public Route(string gtfsLine)
    {
        // Split up the comma-separated values from the GTFS data line
        string[] gtfsElements = gtfsLine.Split(',');

        // Set the ID, Number and Name properties
        this.ID = gtfsElements[0];
        this.Number = Convert.ToInt32(gtfsElements[2]);
        this.Name = gtfsElements[3];

        // Parse the string representing the route color and store it as a Color object
        int rgb = Convert.ToInt32(gtfsElements[7], 16);
        int r = rgb >> 16 & 0xff;
        int g = rgb >> 8 & 0xff;
        int b = rgb & 0xff;
        this.Color = Color.FromArgb(r, g, b);

        // Same as route color but for its text color
        rgb = Convert.ToInt32(gtfsElements[8], 16);
        r = rgb >> 16 & 0xff;
        g = rgb >> 8 & 0xff;
        b = rgb & 0xff;
        this.TextColor = Color.FromArgb(r, g, b);
    }
}

struct Departure 
{
    public int RouteNumber;
    public string Direction;
    public DateTime Time;
    public Color RouteColor;
    public Color RouteTextColor;

    public Departure(Trip trip, DateTime time, List<Route> rl) 
    {
        Route corresponding_route = rl.Find(x => x.ID.Equals(trip.RouteID));
        this.RouteNumber = corresponding_route.Number;
        this.RouteColor = corresponding_route.Color;
        this.RouteTextColor = corresponding_route.TextColor;

        this.Direction = trip.Headsign;
        this.Time = time;
    }

    public override string ToString()
    {
        //return "" + Time.Hour + ":" + Time.Minute + " " + Direction;
        return Time.ToShortTimeString() + " " + Direction;
    }
}

namespace mtdsharp
{
    class GTFSDataSet
    {
        private List<Stop> stop_list;
        public List<Stop> Stops
        {
            get
            {
                return new List<Stop>(stop_list);
            }
        }

        private List<StopTime> stop_time_list;
        private List<Trip> trip_list;
        private List<CalendarDate> calendar_date_list;
        private List<Route> route_list;

        public GTFSDataSet()
        {
            stop_list = new List<Stop>();
            stop_time_list = new List<StopTime>();
            trip_list = new List<Trip>();
            calendar_date_list = new List<CalendarDate>();
            route_list = new List<Route>();

            string[] gtfsStops = File.ReadAllLines("stops.txt");
            for (int i = 1; i < gtfsStops.Length; i++)
            {
                stop_list.Add(new Stop(gtfsStops[i]));
            }

            string[] gtfsStopTimes = File.ReadAllLines("stop_times.txt");
            for (int i = 1; i < gtfsStopTimes.Length; i++)
            {
                stop_time_list.Add(new StopTime(gtfsStopTimes[i]));
            }

            string[] gtfsTrips = File.ReadAllLines("trips.txt");
            for (int i = 1; i < gtfsTrips.Length; i++)
            {
                trip_list.Add(new Trip(gtfsTrips[i]));
            }
            
            string[] gtfsCalendarDates = File.ReadAllLines("calendar_dates.txt");
            for (int i = 1; i < gtfsCalendarDates.Length; i++)
            {
                calendar_date_list.Add(new CalendarDate(gtfsCalendarDates[i]));
            }

            string[] gtfsRoutes = File.ReadAllLines("routes.txt");
            for (int i = 1; i < gtfsRoutes.Length; i++)
            {
                route_list.Add(new Route(gtfsRoutes[i]));
            }
        }

        public Trip GetTrip(StopTime st)
        {
            return trip_list.Find(x => x.ID.Equals(st.TripID));
        }
        
        public Route GetRoute(Trip trip)
        {
            return route_list.Find(x => x.ID.Equals(trip.RouteID));
        }

        public List<Departure> GetDepartures(Stop stop, DateTime start, DateTime end)
        {
            List<Departure> dep_list = new List<Departure>();

            List<StopTime> filtered_stop_time_list = stop_time_list.FindAll(
                s => 
                    s.StopID.Equals(stop.ID) && 
                    s.DepartureTime.CompareTo(start) >= 0 && 
                    s.DepartureTime.CompareTo(end) <= 0);

            foreach (StopTime st in filtered_stop_time_list)
            {
                Trip corresponding_trip = GetTrip(st);
                if (calendar_date_list.FindIndex(cd => (cd.ServiceID.Equals(corresponding_trip.ServiceID) && cd.ServiceDay.Date.Equals(st.DepartureTime.Date))) != -1)
                {
                    dep_list.Add(new Departure(corresponding_trip, st.DepartureTime, route_list));
                }
            }

            dep_list.Sort((a, b) => a.Time.CompareTo(b.Time));

            return dep_list;
        }
    }
}
